/*
 * Copyright 2005-2012 The Kuali Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.opensource.org/licenses/ecl2.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * edoclite validation functions
 * http://www.quirksmode.org/js/introevents.html for background on javascript event quagmire
 *
 * How It All Works
 *
 * This script is called on page load (onPageLoad).  It will register onblur and onchange handlers (probably only
 * onblur is necessary at this time, but it started with just onchange) for fields which have been pre-registered
 * in java script blocks in the incoming document.  The pre-registration supplies a field name which must match a form
 * input name, a regular expression, and an optional error message.  The handlers will fire when the form fields
 * are tabbed-out and unhide a CSS-hidden message row on which a specific CSS style is set depending on warning or error,
 * to color the row appropriately.  A warning/error message is set in this row.  The secret row must have ids like:
 *
 * <fieldname>_message - the message element (typically a <span>)
 * <fieldname>_messageHeaderCell - the "header" cell of the message row (containing label warning/error)
 * <fieldname>_messageDataCell - the "data" cell of the message row (containing the actual message)
 * <fieldname>_messageRow - the entire row for any other decoration
 *
 * The CSS classes set on the header and data cells and message row, are respectively:
 *
 * <msgtype>_messageHeaderCell
 * <msgtype>_messageDataCell
 * <msgtype>_messageRow
 *
 * Where <msgtype> is either "warning", "error", or "empty" (which should just ensure that the elements are hidden regardless).
 *
 * A first-time pass at validation occurs on page load also, to ensure the user is notified ahead of time of field validation
 * requirements.  Fields which do NOT have validation requirements must be carefully omitted from pre-registration, so that fields
 * with outstanding errors generated by, for example, attributes, do NOT have their errors automatically implicitly cleared (the first
 * time pass would otherwise just default to an "accept-anything" regular expression, and immediately wipe out the error generated
 * on the server.  This underlines the importance of ensuring client-side JavaScript-based validation defined in the EDL document
 * is in sync with the ultimate attribute validation (or at least omitted to avoid false positive validation on the client side).
 *
 * Aaron Hamid (arh14 at cornell dot edu) 1/10/06
 */

// ---- globals

var nbsp = 160;    // non-breaking space char

var empty_regex = /^\s*$/;

var field_titles = new Object();
var field_regexes = new Object();
var field_errorMessages = new Object();
var field_validationRequired= new Object();
var buttonTitle; // the global variable
var buttonOnClickFunction; // global variable
var field_customValidators = new Object();
var field_onchange = new Object();

var field_names = new Array;

// ---- utils

function displayNewWindow(url, title, width, height, x, y)
	{
		mywindow = window.open (url, title, status=1, scrollbars=1, location=1, width=width, height=height);
		mywindow.moveTo(x, y);
	}

function confirm_route()
	{
		var route = confirm("Click OK to route this document or Cancel to return.")
		return route;
	}

function replaceEnter(e) {
 	if(window.event) {
    	if(window.event.keyCode == 13) {     	//IE
        	return confirm_route();
    	}
    } else {
        if(e.which == 13) {  	 				//firefox
        	return confirm_route();
		}
    }
}

function isArray(obj)
{
if (isNaN(obj.length))
	return false;
else
	return true;
}

function setTextValue(id, text) {
    var element = document.getElementById(id);
    var node = element.firstChild;
    if (node == null) {
      node = document.createTextNode("");
      element.appendChild(node);
    }
    node.nodeValue = text;
}

function setClass(id, className) {
    document.getElementById(id).className = className;
}

// -----------------------------------------
//                  msg
// Display warn/error message in HTML element
// commonCheck routine must have previously been called
// -----------------------------------------

function setMessage(fld,     // id of element to display message in
             msgtype, // class to give element ("warn" or "error")
             message) // string to display
{
    if (!fld) {
        return;
    }
    // setting an empty string can give problems if later set to a
    // non-empty string, so ensure a space present. (For Mozilla and Opera one could
    // simply use a space, but IE demands something more, like a non-breaking space.)
    var dispmessage;
    if (empty_regex.test(message)) {
        dispmessage = String.fromCharCode(nbsp);
    } else {
        dispmessage = message;
    }

    // set the text value of the message
    setTextValue(fld + "_message", dispmessage);
    // set the text value of the message header cell
    setTextValue(fld + "_messageHeaderCell", msgtype+": ");
    // update messageHeaderCell class
    //setClass(fld + "_messageHeaderCell", msgtype + "_messageHeaderCell");
    // update messageDataCell class
    //setClass(fld + "_messageDataCell", msgtype + "_messageDataCell");
    // update the row class
    //setClass(fld + "_messageRow", msgtype + "_messageRow");
    setClass(fld + "_message", msgtype + "Message");
    setClass(fld+"_messageHeaderCell",msgtype+"Message");
}

function getTarget(event) {
    var target;
    if (event.target) target = event.target;
	  else if (event.srcElement) target = event.srcElement;
    if (target.nodeType == 3) // defeat Safari bug
		    target = target.parentNode;
    return target;
}

function registerHandlers() {
    //alert("Registering handlers");
    // register event handler
    for (var i in field_names) {
        var fieldName = field_names[i];
        if (typeof fieldName == 'function') {
            continue;
        }    
        //alert("registering handler for: " + fieldName);
        var element = document.getElementById('edoclite')[fieldName];
        if (element.length > 1) {
            // for radio buttons (all share the same name)
            for (var j = 0; j < element.length; j++) {
	            // avoid onblur with customValidator 'alert' function because some browsers misbehave (alert repeats perpetually).        
		        if (field_customValidators[fieldName] || field_onchange[fieldName]) {
		            //alert("about to reg cust val " + field_customValidators[fieldName]);
		            element.onchange = executeOnChange;
		        } else {
		            element.onchange = executeOnChange;
		            element.onblur = executeOnChange;
		        }
            }
        }
        // avoid onblur with customValidator 'alert' function because some browsers misbehave (alert repeats perpetually).
        if (field_customValidators[fieldName] || field_onchange[fieldName]) {
            //alert("about to reg cust val " + field_customValidators[fieldName]);
            element.onchange = executeOnChange;
        } else {
            element.onchange = executeOnChange;
            element.onblur = executeOnChange;
        }
        if (element.captureEvents) element.captureEvents(Event.CHANGE);
    }
}

/**
 * Registers an event handler in the "traditional" method
 * and fills in global regex, message, and validationRequired maps
 */
function register(name, title, regex, message, validationRequired) {
    //alert("registering " + name + " " + regex + " " + message + " " + validationRequired);
    addFieldName(name);

    // set the title for this field
    field_titles[name] = title;

    // set the regex for this field
    field_regexes[name] = regex;

    // set the error message for this field
    field_errorMessages[name] = message;

    // set the validation required flag for this field
    field_validationRequired[name] = validationRequired == "true";

	// set the custom validator to be false
    field_customValidators[name] = false;
}

function register_custom(name, title, messages, validationRequired, validationFunction) {
    addFieldName(name);
    field_titles[name] = title;
    field_errorMessages[name] = messages;
    field_validationRequired[name] = validationRequired == "true";
    field_customValidators[name] = validationFunction;
}

function register_onchange(name, onchangeFunction) {
	addFieldName(name);
	field_onchange[name] = onchangeFunction;
}

function addFieldName(fieldName) {
	var alreadyInList = false;
	for (var name in field_names) {
		if (name == fieldName) {
			alreadyInList = true;
			break;
		}
	}
	if (!alreadyInList) {
		field_names.push(fieldName);
	}
}

function isValidationRequired(fieldName) {
    /*if (field_validationRequired[fieldName]) {
      alert("validation required: " + fieldName + " " + field_validationRequired[fieldName]);
    } else {
      //alert("validation NOT required: " + fieldName + " " + field_validationRequired[fieldName]);
    }*/
    return field_validationRequired[fieldName];
}

// ---- validation

function trim(string) {
    return string.replace(/^\s+/m, "") // strip leading
                 .replace(/\s+$/m, ""); // strip trailing
}

function isValid(fieldInputs, regex, required, validator) {
	var fieldValue = getFieldValue(fieldInputs);
    // bypass validation if field has no data and Action is 'Save'.
    if (buttonTitle == 'Save' && (fieldValue == null || trim(fieldValue).length == 0)) {
        return true;
    }    
    if (validator) {
        return validator(fieldValue);
    }
    if (regex == null || regex == "") {
        //no regular expression associated with field
        if (required) {
            //field is required - validate
            return fieldValue != null && trim(fieldValue).length > 0;
        } else {
        	//field not required - do not validate
            return true;
        }
    } else {
    	if (fieldValue == null) {
    		return false;
    	}
    	//regular expression associated with field
        if (required) {
            //field is require - validate
            return fieldValue.match(regex);
        } else {
			//field is NOT required
			if (trim(fieldValue).length > 0) {
				//field is NOT empty - validate
	        	return fieldValue.match(regex);
			} else {
				//field is empty - do NOT validate
				return true;
    }
        }
    }
    //return false;
}

function executeOnChange(event) {
    // event gives access to the event in all browsers
    if (!event) var event = window.event;
	var target = getTarget(event);
	var targetArray = new Array();
	targetArray[0] = target;
    var fieldName = getFieldName(targetArray);
	var onchange = field_onchange[fieldName];
	if (onchange) {
		onchange(event);
	}
	validate(fieldName);
}

//function validate(event, regex, message) {
function validate(fieldName) {
    validateField(getFieldInputs(fieldName), fieldName);
}

function getFieldValue(fieldInputs) {
    // if you check a checkbox and then uncheck it it throws the error below like the length is not updated when you uncheck it.
	if (fieldInputs.length > 1) {
		var type = fieldInputs[0].type;
		if (type == 'radio' || type == 'checkbox') {
			for (var i=0; i < fieldInputs.length; i++){
				if (fieldInputs[i].checked) {
					return fieldInputs[i].value;
				}
			}
			return null;
		}
		throw 'Problem determining field value for inputs, type was: ' + type;
	}
	else {
	    if(fieldInputs[0].type == 'checkbox' && !fieldInputs[0].checked){
			return null;
		}
		return fieldInputs[0].value;
	}
}


function getFieldName(fieldInputs) {
	if (fieldInputs.length > 1) {
		var type = fieldInputs[0].type;
		if (type == 'radio' || type == 'checkbox') {
			for (var i=0; i < fieldInputs.length; i++){
				if (fieldInputs[i].checked) {
					return fieldInputs[i].name;
				}
			}
			if (!fieldInputs[0].disabled) {
				return fieldInputs[0].name;
			}	
		}
		throw 'Problem determining field name for inputs, type was: ' + type;
	}
	else {
		return fieldInputs[0].name;
	}
}


function validateField(fieldInputs, fieldName) {
    try {
        var required = isValidationRequired(fieldName);
        if (!field_customValidators[fieldName]) {
            var regex = field_regexes[fieldName];
            var valid = isValid(fieldInputs, regex, required, false);
        } else {
            var validator = field_customValidators[fieldName]
            var valid = isValid(fieldInputs, "", required, validator)
        }
        var message = "";
        var type = "empty";
        if (!valid) {
            // set a color instead and then pop up summary alert on submit
            // if there are any fields which fail validation
            // but are required="true" in the bizdata
            message = field_errorMessages[fieldName];
            var error_element = document.getElementById(fieldName + "_message");
            if (error_element == null) {
                alert("Could not find error element by id: " + fieldName + "_message");
            } else {
              if (message.length == 0) {
                if (regex == null || regex == "") {
                    var title = field_titles[fieldName];
                    if (title == null) {
                        title = fieldName;
                    }
                    message = "field '" + title + "' is required";
                } else {
                  message = getFieldValue(fieldInputs) + " does not match " + regex;
                }
              }
              if (required) {
                type = "error";
              } else {
                type = "warning";
              }
            }
        }

    } catch (error) {
        message = "Error validating target " + fieldInputs + ": " + error;
        type = "error";
        valid = false;
    }
    setMessage(fieldName, type, message);
    return valid;
}

function validateForm() {

	if ( buttonTitle == 'Cancel' || buttonTitle == 'Disapprove') {
		return 0;
	}

    var errs = 0;
    for (var i in field_names) {
        var fieldName = field_names[i];
        var fieldInputs = getFieldInputs(fieldName);
        // TODO: in the case that there is an error getting the field name or the field name returned by
        // getFieldName(...) does not match the field name above, we are going to suppress validation
        // of the field and continue with the next field.
        //
        // Currently, this is most likely to happen in the case of a "read only" field.  Because disabled
        // input fields are not submitted with the form, we added hidden fields in that case so that you
        // end up with something like the following:
        //
        // <input name="myField" type="hidden" value="..."><input name="myField" type="text" value="..." disabled>
        //
        // As we can see there are 2 input fields so both are returned by calls to getElementById which
        // results in getFieldName throwing an error.  Really, all of this javascript needs to be improved but
        // we need to get this fixed up prior to the 2.2.5 release of KEW and we don't have time to do
        // a hefty refactoring.
        try {
        	var calculatedFieldName = getFieldName(fieldInputs);
        	if (calculatedFieldName == null || calculatedFieldName != fieldName) {
        		continue;
        	}
        } catch (error) {
        	continue;
        }
        if (!validateField(fieldInputs, fieldName)) {
            if (isValidationRequired(fieldName)) {
                errs += 1;
            }
        }
    }
    return errs;
}

function getFieldInputs(fieldName) {
    var edocliteForm = document.getElementById('edoclite');
	var fieldInputs = new Array();
	for(var i = 0; i < edocliteForm.elements.length; i++) {
		if(edocliteForm.elements[i].name == fieldName) {
			fieldInputs[fieldInputs.length] = edocliteForm.elements[i];
		}
	}
	return fieldInputs;
}

function validateOnSubmit(form) {
	// If there is an unsaved note or an unsaved attachment, inform User so it is not lost.
	if ( buttonTitle != 'Cancel' && buttonTitle != 'Disapprove') {
		if (!savenote() || !saveAttachment()) {
		  return false; // submit will not take place.
		}  
    }
	if ( buttonTitle == 'performLookup' ) {
		return true; // when doing a lookup, do not perform validation
	}
	// do form validation.
    var  errs = validateForm();
	if (errs > 1) {
		alert('There are fields which require correction before sending');
	} else if (errs == 1) {
		alert('There is a field which requires correction before sending');
	}
	if (errs > 0){
	  return false; // submit will not take place.
	}
	// Ok no validation errors exist. Next check to see if a custom function should be called.
	// check global var. if exists, call the function. This value can be populated from an onClick function associated with a custom defined button. This allows a submit type button to execute a specific function setting the value of errs which controls whether form is submitted or not.
	if (buttonOnClickFunction) {
	  var copyOfFunction = buttonOnClickFunction;
	  buttonOnClickFunction = ""; // reinitialize function variable
	  //alert('about to execute a function = ' + copyOfFunction);
	  return copyOfFunction();
	} else {
	  return true;
	}  
}

function savenote(){
	if (document.forms[0].elements['addText'] == undefined) {
	  return true;
	}
	if(!isEmpty(document.forms[0].elements['addText'])){
	  alert("In the Create Note box, there is an unsaved note. Save the note by clicking the red Save button to the right of the note, or clear the contents of the note.");
	  return false;
	}else {
	  return true;
	}
}

function isEmpty(aTextField) {
	if (aTextField == undefined) {
	  return true;
	}
	// consider only whitespace characters as empty
	var re = new RegExp(empty_regex);
	if ((aTextField.value.length==0) || (aTextField.value==null) || (aTextField.value.match(re))) {
	  return true;
	}
	else {
	  return false;
	}
}

function saveAttachment(){
	if (document.forms[0].elements['file'] == undefined) {
	  return true;
	}
	if(!isEmpty(document.forms[0].elements['file'])){
	  alert("In the Create Note box, there is an unsaved attachment. Save the file by clicking the red Save button to the right of the file, or clear the contents of the attachment.");
	  return false;
	}else {
	  return true;
	}
}

function buttonClickFunctionName(incomingButtonFunctionName) {
	buttonOnClickFunction = incomingButtonFunctionName;
}

function buttonClick(incomingButtonTitle) {
	buttonTitle = incomingButtonTitle;
}

/**
 * Called when the page is loaded
 * Registers handlers for fields and then
 * performs an initial validation pass (but does not display alert dialog)
 */
function onPageLoad() {
    registerHandlers();
    // commented out for now
    // validateForm();
}
